package com.particles.particles.util;

import android.util.Log;

/**
 * Created by Cuckoo322 on 4/11/2017.
 */

public class Geometry {

    public static class Point {
        public final float x, y, z;

        public Point(float x, float y, float z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        /**
         * 沿着y轴平移
         * @param distance 平移距离
         * @return 返回新的三维空间中的点
         */
        public Point translateY(float distance) {
            return new Point(x, y + distance, z);
        }

        /**
         * 沿着向量平移
         * @param vector
         * @return
         */
        public Point translate(Vector vector) {
            return new Point(x + vector.x, y + vector.y, z + vector.z);
        }
    }

    public static class Circle {
        public final Point center;
        public final float radius;

        public Circle(Point center, float radius) {
            this.center = center;
            this.radius = radius;
        }

        /**
         * 缩放圆
         * @param scale 缩放倍率
         * @return 返回新的圆
         */
        public Circle scale(float scale) {
            return new Circle(center, radius * scale);
        }
    }

    public static class Cylinder {
        public final Point center;
        public final float radius;
        public final float height;

        public Cylinder(Point center, float radius, float height) {
            this.center = center;
            this.radius = radius;
            this.height = height;
        }
    }

    public static class Ray {
        public final Point point;
        public final Vector vector;

        public Ray(Point point, Vector vector) {
            this.point = point;
            this.vector = vector;
        }
    }

    public static class Vector {
        public final float x, y, z;

        public Vector(float x, float y, float z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        /**
         * 计算本向量的模
         * @return
         */
        public float length() {
            return (float) Math.sqrt((x * x) + (y * y) + (z * z));
        }

        /**
         * 计算本向量与另一个传入的向量的交叉乘积
         * 参考：<a href="http://baike.baidu.com/item/%E5%90%91%E9%87%8F%E7%A7%AF?sefr=enterbtn">
         *     向量积（叉乘）
         *     </a>
         *     <br />
         *     <a href="https://en.wikipedia.org/wiki/Cross_product">Cross Product</a>
         * @param other
         * @return
         */
        public Vector crossProduct(Vector other) {
            return new Vector(
                    (y * other.z) - (z * other.y),
                    (z * other.x) - (x * other.z),
                    (x * other.y) - (y * other.x)
            );
        }

        /**
         * 点乘，参考<a href="https://en.wikipedia.org/wiki/Dot_product">Dot product</a>
         * @param other
         * @return
         */
        public float dotProduct(Vector other) {
            return x * other.x + y * other.y + z * other.z;
        }

        /**
         * 均匀缩放向量
         * @param f
         * @return
         */
        public Vector scale(float f) {
            return new Vector(x * f, y * f, z * f);
        }

        /**
         * 向量相加
         * @param other
         * @return 相加后新的向量
         */
        public Vector add(Vector other) {
            return new Vector(x + other.x, y + other.y, z + other.z);
        }
    }

    /**
     * 通过2个点确定一个向量
     * @param from
     * @param to
     * @return
     */
    public static Vector vectorBetween(Point from, Point to) {
        return new Vector(
                to.x - from.x,
                to.y - from.y,
                to.z - from.z
        );
    }

    public static class Sphere {
        public final Point center;
        public final float radius;

        public Sphere(Point center, float radius) {
            this.center = center;
            this.radius = radius;
        }
    }

    /**
     * 若射线到圆心的距离小于圆心半径，则射线与圆球相交（非相切）
     * @param sphere
     * @param ray
     * @return
     */
    public static boolean intersects(Sphere sphere, Ray ray) {
        if (LoggerConfig.DEBUG_ON) {
            Log.d("判断距离：", "" + distanceBetween(sphere.center, ray));
        }
        return distanceBetween(sphere.center, ray) < sphere.radius;
    }

    public static float distanceBetween(Point point, Ray ray) {
        //先构建射线上2个点分别指向另一个点point的向量
        Vector p1ToPoint = vectorBetween(ray.point, point);
        Vector p2ToPoint = vectorBetween(ray.point.translate(ray.vector), point);

        //计算射线与所求距离的点形成的三角形的面积，以及三角形底边的长度
        //由于事实上求三角形的高用的是2倍面积的平行四边形，所以这里就不除以2了
//        float areaOfTriangle = p1ToPoint.crossProduct(p2ToPoint).length() / 2f;
        float areaOfTriangle = p1ToPoint.crossProduct(p2ToPoint).length();
        float lengthOfTriangleBase = ray.vector.length();

        //计算点到射线的距离
//        float distanceFromPointToRay = areaOfTriangle * 2f / lengthOfTriangleBase;
        float distanceFromPointToRay = areaOfTriangle / lengthOfTriangleBase;

        return distanceFromPointToRay;
    }

    public static class Plane {
        //平面上的一个点
        public final Point point;
        //法向量
        public final Vector normal;

        public Plane(Point point, Vector normal) {
            this.point = point;
            this.normal = normal;
        }
    }

    /**
     * 计算射线与平面的交点。计算原理是确定平面上任意一个点和射线上任意一个点，
     * 计算射线点指向平面点的向量，点乘法向量，除以射线向量与法向量的点乘积，
     * 可计算出射线向量到平面向量的放大比例d，并通过d<strong>l</strong>+<strong>l</strong><small>0</small>获得相交点
     * 详见<a href="https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection">Line–plane intersection</a>
     * @param ray
     * @param plane
     * @return
     */
    public static Point intersectionPoint(Ray ray, Plane plane) {
        //计算射线点指向平面点的向量
        Vector rayToPlaneVector = vectorBetween(ray.point, plane.point);

        //计算放大比例d
        float scaleFactor = rayToPlaneVector.dotProduct(plane.normal) /
                ray.vector.dotProduct(plane.normal);

        //找到触点射线与桌面的相交点
        Point intersectionPoint = ray.point.translate(ray.vector.scale(scaleFactor));

        return intersectionPoint;
    }

}
